package com.alibaba.asyncload.impl.spring;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.aop.framework.ProxyConfig;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.util.ClassUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

/**
 * 提供一种机制：无侵入的拦截机制，与现有的bean定义进行融合。实现自定义的{@linkplain BeanPostProcessor}进行替换，部分代码copy
 * from {@linkplain AbstractAutoProxyCreator}
 *
 * <pre>
 * 融合的规则：
 * 1. 原先的bean是{@linkplain ProxyFactoryBean}，则将自己的拦截器定义和proxy bean的定义进行融合,<strong>只是会合并原先的拦截器定义，其他的不做融合</strong>
 *    可通过applyCommonInterceptorsFirst=true/false指定顺序.如果是false则{@linkplain CompositeAutoProxyCreator}定义的拦截器排在后面
 * 2. 如果原先的bean是除{@linkplain ProxyFactoryBean}的bean，则尝试自动创建ProxyFactoryBean，对应的拦截器也仅是所配置的拦截器列表，不会进行自动的扫描和装配
 * 3. 其他的类似：{@linkplain TransactionProxyFactoryBean}并不会进行一个融合的处理
 *
 * </pre>
 *
 * @author jianghang 2011-4-25 上午10:43:12
 */
public class CompositeAutoProxyCreator extends ProxyConfig implements BeanPostProcessor, Ordered,
		BeanClassLoaderAware, BeanFactoryAware, AopInfrastructureBean {

	private static final long serialVersionUID = 8458055362270662345L;
	private static final Field interceptorNamesField = ReflectionUtils
			.findField(ProxyFactoryBean.class, "interceptorNames");

	private ClassLoader proxyClassLoader = ClassUtils.getDefaultClassLoader();
	private boolean classLoaderConfigured = false;
	private BeanFactory beanFactory;
	private List beanNames;
	private int order = Integer.MAX_VALUE;
	private String[] interceptorNames = new String[0];
	private boolean applyCommonInterceptorsFirst = false;
	private final Set nonAdvisedBeans = Collections.synchronizedSet(new HashSet());

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		// 不做处理
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			return wrapIfNecessary(bean, beanName, cacheKey);
		}

		return bean;
	}

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (nonAdvisedBeans.contains(cacheKey)) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass())) {
			nonAdvisedBeans.add(cacheKey);
			return bean;
		}
		// 不能进行代理cache，singleton的实现有spring core核心机制来保证，如果是singleton不会回调多次
		// Create proxy if we have advice.
		if (beanNames != null) {
			for (Iterator it = beanNames.iterator(); it.hasNext();) {
				String mappedName = (String) it.next();
				if (isMatch(beanName, mappedName)) {
					if (ProxyFactoryBean.class.isAssignableFrom(bean.getClass())) {
						ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;
						String[] orignInterceptorNames = getInterceptorFromProxyFactoryBean(
								proxyFactoryBean);
						String[] newInterceptorNames = new String[orignInterceptorNames.length
								+ interceptorNames.length];
						if (applyCommonInterceptorsFirst) {// 如果是true，则将Auto-proxy的拦截器定义到最前面
							// 构造新的的拦截器列表
							System.arraycopy(interceptorNames, 0, newInterceptorNames, 0,
									interceptorNames.length);
							System.arraycopy(orignInterceptorNames, 0, newInterceptorNames,
									interceptorNames.length, orignInterceptorNames.length);
						} else {
							System.arraycopy(orignInterceptorNames, 0, newInterceptorNames, 0,
									orignInterceptorNames.length);
							System.arraycopy(interceptorNames, 0, newInterceptorNames,
									orignInterceptorNames.length, interceptorNames.length);
						}
						// 重新设置新的inteceptorNames
						proxyFactoryBean.setInterceptorNames(newInterceptorNames);
						return proxyFactoryBean;
					} else {
						// 如果是单例，对应的代理bean对象为同一个
						ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
						proxyFactoryBean.setBeanFactory(beanFactory);
						proxyFactoryBean.setBeanClassLoader(proxyClassLoader);
						proxyFactoryBean.setInterceptorNames(interceptorNames);
						proxyFactoryBean.copyFrom(this); // 拷贝对应的一些Proxy config
						proxyFactoryBean.setTarget(bean);
						return proxyFactoryBean.getObject();
					}
				}
			}
		}

		nonAdvisedBeans.add(cacheKey);
		return bean;
	}

	// =========================== helper method
	// ================================

	private String[] getInterceptorFromProxyFactoryBean(ProxyFactoryBean bean) {
		synchronized (interceptorNamesField) {
			try {
				interceptorNamesField.setAccessible(true);
				try {
					Object obj = interceptorNamesField.get(bean);
					return obj != null ? (String[]) obj : new String[0];
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			} finally {
				interceptorNamesField.setAccessible(false);
			}
		}

	}

	/**
	 * 对应的内存cache的key
	 */
	protected Object getCacheKey(Class beanClass, String beanName) {
		return beanClass.getName() + "_" + beanName;
	}

	/**
	 * 不对基础的框架类做auto-proxy
	 * 
	 * @param beanClass
	 * @return
	 */
	protected boolean isInfrastructureClass(Class beanClass) {
		return Advisor.class.isAssignableFrom(beanClass) || Advice.class.isAssignableFrom(beanClass)
				|| AopInfrastructureBean.class.isAssignableFrom(beanClass);
	}

	/**
	 * 返回是否匹配，支持简单的通配符： "xxx*", "*xxx" "*xxx*"
	 */
	protected boolean isMatch(String beanName, String mappedName) {
		return PatternMatchUtils.simpleMatch(mappedName, beanName);
	}

	// ========================= setter / getter ===========================

	public final void setOrder(int order) {
		this.order = order;
	}

	@Override
	public final int getOrder() {
		return order;
	}

	public void setInterceptorNames(String[] interceptorNames) {
		this.interceptorNames = interceptorNames;
	}

	public void setApplyCommonInterceptorsFirst(boolean applyCommonInterceptorsFirst) {
		this.applyCommonInterceptorsFirst = applyCommonInterceptorsFirst;
	}

	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		if (!classLoaderConfigured) {
			proxyClassLoader = classLoader;
		}
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}

	public void setBeanNames(String[] beanNames) {
		this.beanNames = new ArrayList(beanNames.length);
		for (int i = 0; i < beanNames.length; i++) {
			this.beanNames.add(StringUtils.trimWhitespace(beanNames[i]));
		}
	}

}
